package my.design.interpreter;

import java.util.HashMap;
import java.util.Stack;

/**
 * @author zp
 * @version 1.0
 * @description: 计算器解释器
 * 解释器模式定义语言的文法，并且建立一个解释器来解释该语言中的句子。它属于类的行为模式。这里的语言意思是使用规定格式和语法的代码。
 *
 * (1)抽象表达式(Expression)角色：声明一个所有的具体表达式角色都需要实现的抽象接口。这个接口主要是一个interpret()方法，称做解释操作。
 *
 * (2)终结符表达式(Terminal Expression)角色：实现了抽象表达式角色所要求的接口，
 *      主要是一个interpret()方法；文法中的每一个终结符都有一个具体终结表达式与之相对应。比如有一个简单的公式R=R1+R2，
 *      在里面R1和R2就是终结符，对应的解析R1和R2的解释器就是终结符表达式。
 *
 * (3)非终结符表达式(Nonterminal Expression)角色：文法中的每一条规则都需要一个具体的非终结符表达式，
 *      非终结符表达式一般是文法中的运算符或者其他关键字，比如公式R=R1+R2中，“+"就是非终结符，解析“+”的解释器就是一个非终结符表达式。
 *
 * (4)环境(Context)角色：这个角色的任务一般是用来存放文法中各个终结符所对应的具体值，
 *      比如R=R1+R2，我们给R1赋值100，给R2赋值200。这些信息需要存放到环境角色中，很多情况下我们使用Map来充当环境角色就足够了。
 * 需要注意的事项
 *  1、当一个语言需要执行，可以将该语言抽象成一个句子表示的语法树，就可以考虑使用解释器模式，让程序具有良好的扩展性
 *  2、应用场景：编译器 运算表达式计算，正则表达式 机器人
 *  3、可能会带来的问题 容易引发类膨胀的问题 解释器一般采用递归调用 可能会导致运行效率低和调试难度大的问题
 * @date 2022/10/28 18:54
 */
public class Calculator {
    //定义表达式
    private final Expression expression;

    //构造函数传参，并解析
    public Calculator(String expStr) {
        //安排运算先后顺序
        Stack<Expression> stack = new Stack<>();
        //表达式拆分为字符数组
        char[] charArray = expStr.toCharArray();

        Expression left;
        Expression right;
        for (int i = 0; i < charArray.length; i++) {
            switch (charArray[i]) {
                case '+':    //加法
                    left = stack.pop();
                    right = new VarExpression(String.valueOf(charArray[++i]));
                    stack.push(new AddExpression(left, right));
                    break;
                case '-':    //减法
                    left = stack.pop();
                    right = new VarExpression(String.valueOf(charArray[++i]));
                    stack.push(new SubExpression(left, right));
                    break;
                case '*':    //乘法
                    left = stack.pop();
                    right = new VarExpression(String.valueOf(charArray[++i]));
                    stack.push(new MultExpression(left, right));
                    break;
                case '/':    //除法
                    left = stack.pop();
                    right = new VarExpression(String.valueOf(charArray[++i]));
                    stack.push(new DivisionExpression(left, right));
                    break;
                default:  //公式中的变量
                    stack.push(new VarExpression(String.valueOf(charArray[i])));
                    break;
            }
        }
        this.expression = stack.pop();
    }

    //计算
    public Double run(HashMap<String, Double> var) {
        return this.expression.interpreter(var);
    }
}
